/* 
 * Persistence4J - Simple library for data persistence using java
 * Copyright (c) 2010, Avdhesh yadav.
 * http://www.avdheshyadav.com
 * Contact: avdhesh.yadav@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.avdy.p4j.jdbc.dao;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.avdheshyadav.spiderdb.dbmodel.Column;
import com.avdy.p4j.jdbc.common.JdbcSqlUtil;
import com.avdy.p4j.jdbc.dbms.metadata.TableStructure;
import com.avdy.p4j.jdbc.model.DTO;
import com.avdy.p4j.jdbc.service.GenericDAO;
import com.avdy.p4j.jdbc.service.GenericDTO;

/**
 * This class is Thread Safe.
 * 
 * @author Avdhesh Yadav
 *
 */
public class GenericDTORowProcessor implements RowProcessor
{
	static Logger logger = Logger.getLogger(GenericDTORowProcessor.class.getName());
	/** */
	private final int mFetchSize;

	/**
	 * 
	 */
	public GenericDTORowProcessor()
	{
		this(10);
	}
	/**
	 * 
	 * @param tableStructure TableStructure
	 */
	public GenericDTORowProcessor(int fetchSize)
	{
		mFetchSize = fetchSize;
	}
	
	/**
	 * 
	 */
	public List<GenericDTO> toBeanList(TableStructure tableStructure, ResultSet rs, int startPosition, int rows) throws SQLException
	{
		setResultSetAttribute(rs, startPosition);
		if(rows != GenericDAO.ALL_ROWS)
		{
			rows = startPosition + rows -1;
		}
		try 
		{
			List<GenericDTO> retVals = (List<GenericDTO>)copyResultSetToEntityObject(tableStructure, rs, rows);
			return retVals;
		} 
		catch (Exception e) 
		{
			throw new SQLException(e);
		}
	}
	
	/**
	 * 
	 * @param resultSet ResultSet
	 * @param startPosition int
	 * 
	 * @throws SQLException
	 */
	private void setResultSetAttribute(ResultSet resultSet, int startPosition)throws SQLException
	{
		resultSet.setFetchSize(mFetchSize);
		resultSet.relative(startPosition - 1);
	}
	
	
	/**
	 * 
	 * @param tableStructure TableStructure
	 * @param resultSet ResultSet
	 * @param rows int
	 * 
	 * @return List<GenericDTO>
	 * 
	 * @throws SQLException
	 * @throws Exception
	 */
	public List<GenericDTO> copyResultSetToEntityObject(TableStructure tableStructure, ResultSet resultSet,int rows) throws SQLException , Exception
	{
		List<GenericDTO> ll = new ArrayList<GenericDTO>();
		List<Column> coList = tableStructure.getColumns();
		
		GenericDTO gvo = null;
		
		while(resultSet != null && resultSet.next())
		{
			Map<String, Object> data = new HashMap<String, Object>();
			for(Column column : coList)
			{
				String columnName = column.getColumnName().toLowerCase();
				int columnType = column.getDataType().getType();
				Object obj = JdbcSqlUtil.sqlToJava(resultSet, columnType, columnName);
				data.put(columnName, obj);
			}
			gvo = new GenericDTO(tableStructure.getFullTableName(),data);
			ll.add(gvo);
			
			if (resultSet.getRow() == (rows))
			{
				break;
			}
		}
		return ll;
	}
	
	
	/**
	 * This method copies the data to the PreparedStatement.this is simply copy method without involving any Reflection so it is very fast.
	 * 
	 * @param tableStructure TableStructure
	 * @param valueObject DTO
	 * @param pstmt PreparedStatement
	 */
	public void copyEntityObjectToPstmt(TableStructure tableStructure, DTO dto, PreparedStatement pstmt)throws SQLException
	{
		List<Column> columns = tableStructure.getColumns();
		GenericDTO gvo = (GenericDTO)dto;
		
		Map<String, Object> data = gvo.getData();
		
		for(Column column : columns)
		{
			String columnName = column.getColumnName();
			columnName = columnName.toLowerCase();
			Object obj = data.get(columnName);
			int columnType = column.getDataType().getType();
			
			if(obj != null || column.isNullable())
			{
				JdbcSqlUtil.javaToSql(columnType, column.getOrdinalPosition(), obj, pstmt);
			}
			else
			{
				logger.error("null value is not allowed for column: "+ columnName);
			}
		}
	}
}